package com.hioove.core.reflect;

import com.hioove.core.aop.model.InterceptorContext;
import com.hioove.core.model.base.config.ClassConfig;
import com.hioove.core.filter.FilterChain;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Stream;


public class CglibProxy implements MethodInterceptor, Proxy {
    static Logger log = LoggerFactory.getLogger(CglibProxy.class);
    // 用于aop拦截器中间变量
    final static ThreadLocal<InterceptorContext<?>> INTERCEPTOR_CONTEXT = new ThreadLocal<>();

    private List<ClassConfig.MethodSupport> pointcut;
    private List<ClassConfig.MethodSupport> before;
    private List<ClassConfig.MethodSupport> after;

    @Override
    public <T> T create(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //todo  条件过滤
        process(getPointcut());
        process(getBefore());
        // 代理方法执行
        Object proxyResult = proxy.invokeSuper(obj, args);
        process(getAfter());
        // 清理中间变量
        remove();
        return proxyResult;
    }

    private void process(List<ClassConfig.MethodSupport> interceptMission) {
        Optional.ofNullable(interceptMission).orElse(Collections.emptyList()).stream().sorted(Comparator.comparing((ClassConfig.MethodSupport methodSupport) -> methodSupport.getCurrentObj().hashCode())).forEach(methodSupport -> {
            Object[] param = methodSupport.getMethod().getParameterCount() == 0 ? new Object[]{} : new Object[]{Optional.ofNullable(INTERCEPTOR_CONTEXT.get()).orElse(new InterceptorContext<>())};
            try {
                Object invoke = methodSupport.getMethod().invoke(methodSupport.getCurrentObj(), param);
                Stream.of(param.length).filter(integer -> integer != 0).forEach(integer -> INTERCEPTOR_CONTEXT.set((InterceptorContext<?>) invoke));
            } catch (Exception e) {
                log.error("Proxy object exception!!!", e);
            }

        });
    }

    @Override
    public Proxy setPointcut(List<ClassConfig.MethodSupport> pointCut) {
        this.pointcut = pointCut;
        return this;
    }

    @Override
    public Proxy setBefore(List<ClassConfig.MethodSupport> before) {
        this.before = before;
        return this;
    }

    @Override
    public Proxy setAfter(List<ClassConfig.MethodSupport> after) {
        this.after = after;
        return this;
    }

    private List<ClassConfig.MethodSupport> getPointcut() {
        return pointcut;
    }

    private List<ClassConfig.MethodSupport> getBefore() {
        return before;
    }

    private List<ClassConfig.MethodSupport> getAfter() {
        return after;
    }

    public static void remove() {
        INTERCEPTOR_CONTEXT.remove();
    }

}
